package com.yhb.service.impl;

import com.yhb.channel.ChannelFactory;
import com.yhb.channel.exception.CardAlreadyBoundException;
import com.yhb.channel.model.IBankCard;
import com.yhb.channel.provider.IUserBankCardProvider;
import com.yhb.conf.ResultCode;
import com.yhb.dao.CardMapper;
import com.yhb.model.Aliyun.CheckCardResp;
import com.yhb.model.BranchBank;
import com.yhb.model.Card;
import com.yhb.model.User;
import com.yhb.service.CardService;
import com.yhb.service.UserService;
import com.yhb.utils.StringUtils;
import com.yhb.yinsheng.UnionPayService;
import lombok.extern.log4j.Log4j;
import lombok.var;
import me.edzh.spring.bootstrap.service.ObjectiveStorageService;
import me.edzh.spring.bootstrap.service.SMSService;
import me.edzh.spring.bootstrap.utils.JSONUtil;
import me.edzh.spring.bootstrap.utils.MathUtils;
import me.edzh.spring.bootstrap.utils.exception.ServiceException;
import me.edzh.spring.bootstrap.utils.http.ClientHolder;
import okhttp3.HttpUrl;
import okhttp3.Request;
import org.apache.http.util.TextUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Log4j
public class CardServiceImpl implements CardService, IUserBankCardProvider {

    @Autowired
    private ChannelFactory channelFactory;

    @Autowired
    private CardMapper cardMapper;

    @Autowired
    private ObjectiveStorageService ossServiceImpl;

    @Autowired
    @Lazy
    private UnionPayService unionPayService;

    @Autowired
    private RedisTemplate<String, String> redis;

    @Autowired
    private SMSService smsService;

    @Autowired
    private UserService userService;

    private final static String PREFIX = "card-service-";
    private final static String CHANGE_PHONE_NUMBER_PREFIX = "change-card-phone-number-";

    private final static List<String> directedManagedCities = Arrays.asList("北京市",
            "上海市",
            "重庆市",
            "天津市");


    @Override
    public Card card(String cardId) {
        return cardMapper.getCard(cardId);
    }


    @Override
    public Card cardWithOwnerCheck(String cardId, String userId) {
        if (!StringUtils.isUUID(cardId)) throw new ServiceException(ResultCode.ERROR_PARAMETER_WRONG, "empty cardId");

        Card card = card(cardId);
        if (card.getUserId().equals(userId) && card.getDeleted() != 1) {
            return card;
        }

        throw new ServiceException(ResultCode.ERROR_PARAMETER_WRONG, "卡信息不正确");
    }

    @Override
    public String bindCard(Card card, User user) {
        var c = addCard(card, user.getId());
        String token = UUID.randomUUID().toString();
        String verifyCode = (int)(MathUtils.random(1., 9.) * 100000) + "";
        card.setPayDay(verifyCode); /** 懒得起字段了, 就复用一下这个吧 */
        redis.boundValueOps(PREFIX + token).set(JSONUtil.toJson(card), 10, TimeUnit.MINUTES);
        Map<String, String> p = new HashMap<>();
        p.put("code", verifyCode);
        smsService.sendSms(card.getPhoneNumber(), "SMS_185230020", p);
        return token;


//        if (c.getDeleted() == 1) { // 逻辑删除的卡重新绑定的情况
//            String token = UUID.randomUUID().toString();
//            String verifyCode = (int)(MathUtils.random(1., 9.) * 100000) + "";
//            card.setPayDay(verifyCode); /** 懒得起字段了, 就复用一下这个吧 */
//            redis.boundValueOps(PREFIX + token).set(JSONUtil.toJson(card), 10, TimeUnit.MINUTES);
//            Map<String, String> p = new HashMap<>();
//            p.put("code", verifyCode);
//            smsService.sendSms(card.getPhoneNumber(), "SMS_185230020", p);
//            return token;
//        } else { // 全新的卡
//            if (card.getType() == Card.Type.DEBIT) {
//                card.setPositiveImg(ossServiceImpl.signUrl(card.getPositiveImg()));
//            }
//            try {
//                var result = unionPayService.bindCard(card.getId(), user);
//                return result.getToken();
//            } catch (CardAlreadyBoundException cbe) {
//                card.setStatus(Card.Status.BOUND);
//                modifyCard(card);
//                throw new ServiceException(ResultCode.ERROR_CARD_DUPLICATED_ERR, "此卡已绑定");
//
//            } catch (ServiceException e) {
//                cardMapper.removeCard(card.getId());
//                throw e;
//
//            } catch (Exception e) {
//                log.error("绑卡失败", e);
//                cardMapper.removeCard(card.getId());
//                throw new ServiceException(ResultCode.ERROR_FAILED_TO_APPLY_VERIFY_CODE_ERR, "申请绑卡短信失败");
//            }
//        }

    }

    @Override
    public void verifyCard(String token, String verifyCode, User user) {
        String key = PREFIX + token;

        if (redis.hasKey(key)) {
            var c = JSONUtil.parseJson(redis.boundValueOps(key).get(), Card.class);

            var oc = cardMapper.getCardByNumber(c.getNumber());
            checkUserCard(user, c);
            modifyCard(Card.builder()
                    .id(oc.getId())
                    .deleted(0)
                    .phoneNumber(c.getPhoneNumber())
                    .positiveImg(c.getPositiveImg())
                    .subBankName(c.getSubBankName())
                    .bankName(c.getBankName())
                    .cvv2(c.getCvv2())
                    .city(c.getCity())
                    .province(c.getProvince())
                    .district(c.getDistrict())
                    .status(Card.Status.BOUND)
                    .validation(c.getValidation())
                    .build());

            if (oc.getType() == Card.Type.DEBIT) {
                var defaultCard = userDefaultCard(user);
                if (defaultCard == null) {
                    setDefaultCard(oc.getId(), user.getId());
                }
            }
            return;
        }

        throw new ServiceException(ResultCode.ERROR_TOAST_ERR, "验证码过期");
//
//
//        if (redis.hasKey(key)) { // 这种情况是逻辑删除重绑的
//            var newC = JSONUtil.parseJson(redis.boundValueOps(key).get(), Card.class);
//            if (!newC.getPayDay().equals(verifyCode)) throw new ServiceException(ResultCode.ERROR_TOAST_ERR, "验证码无效");
//
//            var oldC = cardMapper.getCardByNumber(newC.getNumber());
//            redis.delete(key);
//
//            if (newC.getType() == Card.Type.DEBIT) { // 借记卡只要验证码通过即可
//                modifyCard(Card.builder()
//                        .id(oldC.getId())
//                        .phoneNumber(newC.getPhoneNumber())
//                        .positiveImg(newC.getPositiveImg())
//                        .subBankName(newC.getSubBankName())
//                        .deleted(0)
//                        .build());
//            } else {
//                Optional.of(newC)
//                        .filter(c -> oldC.getCvv2().equals(c.getCvv2()))
//                        .filter(c -> oldC.getValidation().equals(c.getValidation()))
//                        .orElseThrow(() -> new ServiceException(ResultCode.ERROR_TOAST_ERR, "卡信息不正确"));
//
//                modifyCard(Card.builder()
//                        .id(oldC.getId())
//                        .phoneNumber(newC.getPhoneNumber())
//                        .deleted(0)
//                        .build());
//
//                if (oldC.getType() == Card.Type.DEBIT) {
//                    var defaultCard = userDefaultCard(user);
//                    if (defaultCard == null) {
//                        setDefaultCard(oldC.getId(), user.getId());
//                    }
//                }
//            }
//        } else {
//            var card = unionPayService.verifyBindCard(token, verifyCode, user);
//            card.setStatus(Card.Status.BOUND);
//            modifyCard(card);
//
//            if (card.getType() == Card.Type.DEBIT) {
//                var defaultCard = userDefaultCard(user);
//                if (defaultCard == null) {
//                    setDefaultCard(card.getId(), user.getId());
//                }
//            }
//        }


    }

    @Override
    public void modifyCard(Card card) {
        if (cardMapper.modifyCard(card) == 0) {
            throw new ServiceException(ResultCode.ERROR_PARAMETER_WRONG, "更新卡信息失败");
        }
    }

    @Override
    @Transactional
    public Card addCard(Card card, String userId) {
        if (card.getType() == Card.Type.DEBIT) {
            if (TextUtils.isEmpty(card.getProvince())) {
                throw new ServiceException(ResultCode.ERROR_PARAMETER_WRONG, "missing province");
            }

            if (TextUtils.isEmpty(card.getCity())) {
                throw new ServiceException(ResultCode.ERROR_PARAMETER_WRONG, "missing city");
            }

            if (TextUtils.isEmpty(card.getSubBankName())) {
                throw new ServiceException(ResultCode.ERROR_PARAMETER_WRONG, "missing subBankName");
            }
        }


        if (TextUtils.isEmpty(card.getBankName())) {
            throw new ServiceException(ResultCode.ERROR_PARAMETER_WRONG, "missing bankName");
        }

        Card c = cardMapper.getCardByNumber(card.getNumber());
        if (c != null) {
            if(c.getStatus() == Card.Status.BOUND && c.getDeleted() != 1) throw new ServiceException(ResultCode.ERROR_CARD_DUPLICATED_ERR, "此卡已绑定");
            if(!c.getUserId().equals(userId)) throw new ServiceException(ResultCode.ERROR_TOAST_ERR, "此卡已被其他用户绑定");

            card.setId(c.getId());
            cardMapper.modifyCard(card);
            return c;

        }
        card.setUserId(userId);
        card.setStatus(Card.Status.UNBOUND);
        card.setDeleted(0);
        cardMapper.addCard(card);

        return card;
    }

    @Override
    public IBankCard getBankCard(String cardId) {
        return card(cardId);
    }

    @Override
    public List<Card> getUserCard(String userId) {
        List<Card> cards = cardMapper.getCardByUserId(userId);

        return cards.parallelStream()
                .filter(card -> card.getStatus() == Card.Status.BOUND && card.getDeleted() != 1)
                .map(this::maskedCard)
                .collect(Collectors.toList());
    }

    @Override
    public Card maskedCard(Card card) {
        return Card.builder()
                .id(card.getId())
                .number(StringUtils.getMaskCardNumber(card.getNumber()))
                .type(card.getType())
                .bankName(card.getBankName())
                .phoneNumber(card.getPhoneNumber())
                .status(card.getStatus())
                .isDefaultCard(card.getIsDefaultCard())
                .build();
    }

    @Override
    public void setDefaultCard(String cardId, String userId) {
        Card card = cardWithOwnerCheck(cardId, userId);
        if (card.getType() == Card.Type.CREDIT) throw new ServiceException(ResultCode.ERROR_TOAST_ERR, "无法将信用卡设为默认结算卡");

        List<Card> cards = cardMapper.getCardByUserId(userId);
        for (Card c: cards) {
            if (c.getId().equals(card.getId())) {
                cardMapper.modifyCard(Card.builder().id(c.getId()).isDefaultCard(1).build());
            } else {
                cardMapper.modifyCard(Card.builder().id(c.getId()).isDefaultCard(0).build());
            }
        }
    }

    @Override
    public List<BranchBank> searchSubBank(String bankName, String province, String city) {
        if (directedManagedCities.contains(province)) {
            city = province;
        }
        return Optional.ofNullable(cardMapper.searchSubBanks(bankName, province, city)).orElse(new ArrayList<>());
    }

    @Override
    public void deleteCard(String cardId, String userId) {
        var c = cardWithOwnerCheck(cardId, userId);
        if (c.getType() == Card.Type.DEBIT && Integer.valueOf(1).equals(c.getIsDefaultCard())) {
            throw new ServiceException(ResultCode.ERROR_TOAST_ERR, "请勿删除默认结算卡");
        }
        modifyCard(Card.builder()
                .id(c.getId())
                .deleted(1)
                .build());
    }

    @Override
    public String getChangeCardPhoneNumberSms(String cardId, String userId, String phoneNumber) {
        var card = cardWithOwnerCheck(cardId, userId);

        String token = UUID.randomUUID().toString();
        String verifyCode = (int) (MathUtils.random(1., 9.) * 100000) + "";
        var model = Card.builder()
                .phoneNumber(phoneNumber)
                .id(card.getId())
                .validation(verifyCode)
                .build();

        redis.boundValueOps(CHANGE_PHONE_NUMBER_PREFIX + token).set(JSONUtil.toJson(model), 10, TimeUnit.MINUTES);
        Map<String, String> p = new HashMap<>();
        p.put("code", verifyCode);
        smsService.sendSms(phoneNumber, "SMS_159530057", p);
        return token;
    }

    @Override
    public void verifyChangeCardNumber(String smsToken, String verifyCode, String userId) {
        String modelString = redis.boundValueOps(CHANGE_PHONE_NUMBER_PREFIX + smsToken).get();
        if (modelString == null) {
            throw new ServiceException(ResultCode.ERROR_TOAST_ERR, "验证码无效或已过期");
        }


        var model = JSONUtil.parseJson(modelString, Card.class);
        if (!model.getValidation().equals(verifyCode))
            throw new ServiceException(ResultCode.ERROR_TOAST_ERR, "验证码无效或已过期");

        var card = cardWithOwnerCheck(model.getId(), userId);

        redis.delete(CHANGE_PHONE_NUMBER_PREFIX + smsToken);

        modifyCard(Card.builder()
                .id(card.getId())
                .phoneNumber(model.getPhoneNumber())
                .build());

    }

    @Override
    public Card userDefaultCard(User user) {
        return userDefaultCard(user.getId());
    }

    public Card userDefaultCard(String userId) {
        var cards = getUserCard(userId);
        for (var c : cards) {
            if (c.getType() == Card.Type.DEBIT) {
                return c;
            }
        }
        throw new ServiceException(ResultCode.ERROR_TOAST_ERR, "请先绑定一张储蓄卡");
    }

    @Override
    public IBankCard getUserDebitCard(String userId) {
        return userDefaultCard(userId);
    }

    private void checkUserCard(User u, Card c) {
        var certificate = userService.getCertificate(u.getId());
        var url = HttpUrl.parse("https://bcard3and4.market.alicloudapi.com/bankCheck4New")
                .newBuilder()
                .addQueryParameter("accountNo", c.getNumber())
                .addQueryParameter("idCard", certificate.getIdCode())
                .addQueryParameter("mobile", c.getPhoneNumber())
                .addQueryParameter("name", certificate.getName())
                .build();
        var req = new Request.Builder()
                .addHeader("Authorization", "APPCODE 9b67f333cbf044088dd84f80f1a863e7")
                .get()
                .url(url)
                .build();
        try {
            var resp = ClientHolder.getClient().newCall(req).execute();
            var respStr = Objects.requireNonNull(resp.body()).string();
            log.info("req to aliyun card check, resp: " + respStr);
            var respObj = JSONUtil.parseJson(respStr, CheckCardResp.class);
            if (!"01".equals(respObj.getStatus())) {
                throw new ServiceException(ResultCode.ERROR_TOAST_ERR, "绑卡失败:" + respObj.getMsg());
            }
        } catch (NullPointerException | IOException t) {
            throw new ServiceException(ResultCode.ERROR_TOAST_ERR, "后端鉴权失败, 请稍后重试");
        }



    }

    @Override
    public IBankCard getBankCardByCardNumber(String cardNumber) {
        return cardMapper.getCardByNumber(cardNumber);
    }
}
